package com.yanghui.dtracing.core.plugin.interceptor;

import com.yanghui.dtracing.core.exception.PluginException;
import com.yanghui.dtracing.core.log.ILog;
import com.yanghui.dtracing.core.log.LogManager;
import net.bytebuddy.implementation.bind.annotation.*;

import java.lang.reflect.Method;

public class InstanceMethodInterceptWithOverrideArgs {

    private static final ILog log = LogManager.getLogger(InstanceMethodInterceptWithOverrideArgs.class);

    private final IInstanceMethodAroundInterceptor interceptor;

    public InstanceMethodInterceptWithOverrideArgs(String instanceMethodAroundInterceptorClassName, ClassLoader classLoader){
        try{
            this.interceptor = InterceptorInstanceLoader.load(instanceMethodAroundInterceptorClassName,classLoader);
        }catch (Throwable t){
        	log.error("InstanceMethodInterceptWithOverrideArgs load fail.", t);
            throw new PluginException("Can't create InstanceMethodInterceptWithOverrideArgs.",t);
        }
    }

    @RuntimeType
    public final Object intercept(@This Object thisObj, @AllArguments Object[] allArguments,
                                  @Origin Method method,
                                  @Morph OverrideArgsCallable overrideArgsCallable) throws Throwable{
        /***
         * 判断是否拦截
         */
        boolean isIntercept = true;
        try{
            isIntercept = this.interceptor.isIntercept(thisObj,method,allArguments);
        }catch (Throwable t){
            log.error("class[{}] isIntercept method[{}] intercept failure", thisObj.getClass(), method.getName(),t);
        }
        if(!isIntercept){
            return overrideArgsCallable.call(allArguments);
        }

        MethodInterceptResult methodInterceptResult = new MethodInterceptResult();
        try{
            this.interceptor.beforeMethod(thisObj,method,allArguments,methodInterceptResult);
        }catch (Throwable t){
            log.error("InstanceMethodInterceptWithOverrideArgs：class[{}] before method[{}] intercept failure", thisObj.getClass(), method.getName(),t);
        }
        Object result;
        try {
            /**调用目标方法**/
            result = overrideArgsCallable.call(allArguments);
            methodInterceptResult.setResult(result);
        }catch (Throwable t){
            try {
                methodInterceptResult.setHasException(true);
                this.interceptor.handleMethodException(thisObj,method,allArguments,t,methodInterceptResult);
            }catch (Throwable throwable){
            	log.error("InstanceMethodInterceptWithOverrideArgs：class[{}] exception method[{}] intercept failure",thisObj.getClass(), method.getName(),throwable);
            }
            throw t;
        }finally {
            try {
                this.interceptor.afterMethod(thisObj,method,allArguments,methodInterceptResult);
                result = methodInterceptResult.getResult();
            }catch (Throwable t){
                log.error("InstanceMethodInterceptWithOverrideArgs：class[{}] after method[{}] intercept failure",thisObj.getClass(), method.getName(),t);
            }
        }
        return result;
    }
}
